package web

import (
	"commerce/cache"
	"commerce/common"
	"commerce/data"
	"commerce/model"
	"commerce/req"
	"commerce/service"
	"commerce/utils"
	"commerce/vo"
	"commerce/ws"
	"encoding/json"
	"fmt"
	"math"
	"net/http"
	"strconv"
	"time"
)

/* 此文件是 app 订单&支付 请求 */

func SubmitOrder(w http.ResponseWriter, r *http.Request) {

	member := extractUserFromContext(r)
	if member == nil || member.Mid <= 0 {
		common.DisplayTokenErr(w, http.StatusUnauthorized)
		return
	}
	var orderReq req.SubmitOrderReq
	err := json.NewDecoder(r.Body).Decode(&orderReq)
	if err != nil {
		common.Error.Printf("SubmitOrder err:%v", err)
		j, _ := json.Marshal(vo.Err(err.Error()))
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusOK)
		w.Write(j)
		return
	}
	// 校验必填字段
	err = checkInputField(orderReq)
	if err != nil {
		common.Error.Printf("1.SubmitOrder err:%v", err)
		j, _ := json.Marshal(vo.Err(err.Error()))
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusOK)
		w.Write(j)
		return
	}
	// 原子令牌验证
	result, err := cache.ReleaseLock(common.ORDER_TOKEN_PREFIX+strconv.Itoa(member.Mid), orderReq.OrderToken, common.ReleaseLockLuaScript)
	if err != nil || result.(int) != 1 {
		common.Error.Printf("2.SubmitOrder err:%v", err)
		j, _ := json.Marshal(vo.OrderTokenErr("页面数据失效，请刷新重试！"))
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusOK)
		w.Write(j)
		return
	}
	orderReq.MemberId = member.Mid
	orderDTO, err := createOrder(orderReq)
	if err != nil {
		common.Error.Printf("2.SubmitOrder err:%v", err)
		j, _ := json.Marshal(vo.Err(err.Error()))
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusOK)
		w.Write(j)
		return
	}
	order := orderDTO.Order
	// 验价
	if math.Abs(order.PayAmount-orderReq.PayPrice) >= 0.01 {
		// 验价不通过
		common.Error.Printf("3.SubmitOrder err:%v", err)
		j, _ := json.Marshal(vo.OrderTokenErr("页面数据过期，请下拉刷新重试！"))
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusOK)
		w.Write(j)
		return
	}
	// 保存订单&及订单项
	orderId, err := saveOrderAndItems(orderDTO)
	if err != nil {
		common.Error.Printf("4.SubmitOrder err:%v", err)
		j, _ := json.Marshal(vo.OrderTokenErr(err.Error()))
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusOK)
		w.Write(j)
		return
	}
	if orderReq.Type == 0 {
		// 清空直接购买的缓存
		data.ClearBuyCart(member.Mid)
	} else {
		// @ todo 暂时先不清除，方便测试
		//data.ClearCart(member.Mid)
	}
	// 推送通知
	go pushNewOrderMsg()

	j, _ := json.Marshal(vo.Ok(orderId))
	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(http.StatusOK)
	w.Write(j)
}

func pushNewOrderMsg() {
	ws.H.GetBroadcast() <- []byte("n")
	// 播放声音提醒 @ todo
}

func ListOrder(w http.ResponseWriter, r *http.Request) {

	member := extractUserFromContext(r)
	if member == nil || member.Mid <= 0 {
		common.DisplayTokenErr(w, http.StatusUnauthorized)
		return
	}
	page, _ := strconv.Atoi(r.PostFormValue("page"))
	if page == 0 {
		page = 1
	}
	size, _ := strconv.Atoi(r.PostFormValue("size"))
	if size == 0 {
		size = 10
	}

	orderPage, err := service.ListMyOrder(member.Mid, page, size)

	if err != nil {
		common.Error.Printf("ListOrder err:%v", err)
		j, _ := json.Marshal(vo.Err(err.Error()))
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusOK)
		w.Write(j)
		return
	}
	j, _ := json.Marshal(vo.Ok(orderPage))
	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(http.StatusOK)
	w.Write(j)
}

func OrderDetail(w http.ResponseWriter, r *http.Request) {

	member := extractUserFromContext(r)
	if member == nil || member.Mid <= 0 {
		common.DisplayTokenErr(w, http.StatusUnauthorized)
		return
	}
	orderId, err := strconv.Atoi(r.PostFormValue("orderId"))
	if err != nil || orderId <= 0 {
		common.Error.Printf("OrderDetail err:%v", err)
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusBadRequest)
		return
	}
	detailVo, err := service.AppOrderDetail(orderId, member.Mid)
	if err != nil {
		common.Error.Printf("2.OrderDetail err:%v", err)
		j, _ := json.Marshal(vo.Err(err.Error()))
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusOK)
		w.Write(j)
		return
	}
	j, _ := json.Marshal(vo.Ok(detailVo))
	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(http.StatusOK)
	w.Write(j)
}

func CancelOrder(w http.ResponseWriter, r *http.Request) {

	member := extractUserFromContext(r)
	if member == nil || member.Mid <= 0 {
		common.DisplayTokenErr(w, http.StatusUnauthorized)
		return
	}
	orderId, err := strconv.Atoi(r.PostFormValue("orderId"))
	if err != nil || orderId <= 0 {
		common.Error.Printf("CancelOrder err:%v", err)
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusBadRequest)
		return
	}
	err = service.CancelOrder(orderId, member.Mid)
	if err != nil {
		common.Error.Printf("2.CancelOrder err:%v", err)
		j, _ := json.Marshal(vo.Err(err.Error()))
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusOK)
		w.Write(j)
		return
	}
	j, _ := json.Marshal(vo.Ok("取消成功！"))
	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(http.StatusOK)
	w.Write(j)
}

// 确认收货

func ConfirmOrderGoods(w http.ResponseWriter, r *http.Request) {

	member := extractUserFromContext(r)
	if member == nil || member.Mid <= 0 {
		common.DisplayTokenErr(w, http.StatusUnauthorized)
		return
	}
	orderId, err := strconv.Atoi(r.PostFormValue("orderId"))
	if err != nil || orderId <= 0 {
		common.Error.Printf("CancelOrder err:%v", err)
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusBadRequest)
		return
	}
	err = service.ConfirmOrderGoods(orderId, member.Mid)
	if err != nil {
		common.Error.Printf("2.CancelOrder err:%v", err)
		j, _ := json.Marshal(vo.Err(err.Error()))
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusOK)
		w.Write(j)
		return
	}
	j, _ := json.Marshal(vo.Ok("收货成功！"))
	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(http.StatusOK)
	w.Write(j)
}

func saveOrderAndItems(dto *req.OrderDTO) (int, error) {

	dto.Order.CreateTime = time.Now().Add(8 * time.Hour)
	dto.Order.UpdatedTime = dto.Order.CreateTime

	return service.SaveOrder(dto)
}

func checkInputField(orderReq req.SubmitOrderReq) error {

	if len(orderReq.OrderToken) == 0 {
		return fmt.Errorf("缺少orderToken")
	}
	if orderReq.AddressId <= 0 {
		return fmt.Errorf("缺少地址信息")
	}
	if orderReq.PayPrice <= 0 {
		return fmt.Errorf("缺少应付金额")
	}
	return nil
}

func createOrder(orderReq req.SubmitOrderReq) (*req.OrderDTO, error) {

	var dto req.OrderDTO

	orderModel, err := buildOrder(orderReq)
	if err != nil {
		return nil, err
	}
	// 订单
	dto.Order = *orderModel

	// 订单项列表
	orderItemList, err := buildOrderItems(orderModel.OrderNo, orderReq.MemberId, orderReq.Type)
	if err != nil || len(orderItemList) == 0 {
		if err != nil {
			return nil, err
		}
		return nil, fmt.Errorf("购物车为空！请重新挑选！")
	}
	dto.OrderItems = orderItemList

	// 金额计算
	calcPrice(orderModel, orderItemList)

	dto.Order = *orderModel

	return &dto, nil
}

func calcPrice(orderModel *model.Order, list []model.OrderItem) {

	var total float64 = 0
	var discountAmount float64 = 0
	var integrationAmount float64 = 0
	var couponAmount float64 = 0

	var count int = 0

	var giftIntegration = 0
	var giftGrowth = 0

	for _, item := range list {

		total += item.RealAmount
		discountAmount += item.DiscountAmount
		integrationAmount += item.IntegrationAmount
		couponAmount += item.CouponAmount

		count += item.Count

		giftIntegration += item.GiftIntegration
		giftGrowth += item.GiftGrowth
	}
	// 此订单下所有商品本身应付价格之和（每件商品已经扣除了各种优惠后 的累积）
	orderModel.TotalAmount, _ = strconv.ParseFloat(fmt.Sprintf("%.2f", total), 64)
	orderModel.TotalCount = count

	// 优惠 @todo 后续真有值是处理2位小数的问题
	orderModel.CouponAmount = couponAmount
	orderModel.IntegrationAmount = integrationAmount
	orderModel.DiscountAmount = discountAmount

	// 实际应付：商品应付价格 + 运费
	orderModel.PayAmount, _ = strconv.ParseFloat(fmt.Sprintf("%.2f", orderModel.TotalAmount+orderModel.FreightAmount), 64)

	// 成长值赠送
	orderModel.GiftIntegration = giftIntegration
	orderModel.GiftGrowth = giftGrowth
}

func buildOrderItems(orderNo string, memberId int, buyType int8) ([]model.OrderItem, error) {

	var cart *vo.Cart
	var err error
	// 直接购买
	if buyType == 0 {
		cart, err = data.ListBuyCheckedItems(memberId)
	} else {
		// 通过购物车
		cart, err = data.ListCheckedItems(memberId)
	}
	if err != nil {
		return nil, err
	}
	var items []model.OrderItem
	for _, item := range cart.CartItems {

		orderItem := model.OrderItem{OrderNo: orderNo}
		// 商品+品牌+分类 信息
		sku, _ := data.GetSkuById(item.SkuId)
		if sku != nil {
			orderItem.SpuId = sku.GoodsId
			orderItem.SpuName = sku.GoodsName
			orderItem.BrandId = sku.BrandId
			orderItem.CategoryId = sku.CategoryId
		}
		orderItem.SkuId = item.SkuId
		orderItem.SkuName = item.SkuName
		orderItem.SkuImg = item.SkuImg
		orderItem.SkuPrice = item.Price
		orderItem.Count = item.Count
		orderItem.SkuAttrValue = item.SpecNameAttrs

		// 优惠券 。。。 暂时没有
		orderItem.CouponAmount = 0

		// 暂时也不让用户消费积分，后续接入@todo
		orderItem.Integration = 0
		orderItem.IntegrationAmount = 0

		amount := item.GetAmount()
		orderItem.Amount = amount

		// 但是成长积分肯定是要给到用户的
		orderItem.GiftIntegration = int(amount)
		orderItem.GiftGrowth = orderItem.GiftIntegration
		// 优惠金额@ todo 后续真有值时，处理2位小数的问题，可参看 下面
		orderItem.DiscountAmount = orderItem.IntegrationAmount + orderItem.CouponAmount
		// 实际应付 = 商品价格 - 优惠金额
		orderItem.RealAmount = amount - orderItem.DiscountAmount
		orderItem.RealAmount, _ = strconv.ParseFloat(fmt.Sprintf("%.2f", orderItem.RealAmount), 64)

		items = append(items, orderItem)
	}
	return items, nil
}

func buildOrder(orderReq req.SubmitOrderReq) (*model.Order, error) {

	var order model.Order

	order.OrderNo = utils.GenerateOrderNo()
	order.State = common.ORDER_STATE_CREATED
	order.IsValid = 1
	order.AutoConfirmDay = 7
	order.UserId = orderReq.MemberId

	// 查 地址
	addr, err := data.GetMemberReceiveAddrById(orderReq.AddressId)
	if err != nil {
		return nil, err
	}
	order.ReceiverUserName = addr.Name
	order.ReceiverProvince = addr.Province
	order.ReceiverCity = addr.City
	order.ReceiverRegion = addr.Region
	order.ReceiverDetailAddr = addr.DetailAddr
	order.ReceiverPhone = addr.Phone
	order.ReceiverPostCode = addr.PostCode
	// 查询运费 @ todo
	order.FreightAmount = 5

	return &order, nil
}
